use crate::{Solution, SolutionPair};
use std::{collections::HashSet, fs::read_to_string};

fn calc_priority(item_type: char) -> u32 {
    if item_type.is_lowercase() {
        ((item_type as u32) - ('a' as u32)) + 1
    } else if item_type.is_uppercase() {
        ((item_type as u32) - ('A' as u32)) + 27
    } else {
        panic!();
    }
}

struct Rucksack {
    item_type: char,
}

impl Rucksack {
    fn new(input: &str) -> Self {
        let len = input.len();
        let (first_part, second_part) = input.split_at(len / 2);
        let first_part = first_part.chars().collect::<HashSet<_>>();
        let second_part = second_part.chars().collect::<HashSet<_>>();
        let difference = first_part.intersection(&second_part).collect::<Vec<_>>();

        Self {
            item_type: *difference[0],
        }
    }
}

struct ElfGroup {
    badge_type: char,
}

impl ElfGroup {
    fn new(input: &[&str; 3]) -> Self {
        let [line1, line2, line3] = *input;

        let found_char = line1
            .chars()
            .find(|c| line2.contains(*c) && line3.contains(*c))
            .unwrap();

        Self {
            badge_type: found_char,
        }
    }
}

pub fn solve() -> SolutionPair {
    let input = read_to_string("input/day03.txt").unwrap();

    let sol1: u32 = solve_part1(String::from(&input));
    let sol2: u32 = solve_part2(String::from(&input));

    (Solution::from(sol1), Solution::from(sol2))
}

fn solve_part1(input: String) -> u32 {
    input
        .lines()
        .map(|items_str| calc_priority(Rucksack::new(items_str).item_type))
        .sum()
}

fn solve_part2(input: String) -> u32 {
    let mut current_elf_group = [""; 3];
    let mut elf_groups: Vec<ElfGroup> = Vec::new();

    input.lines().enumerate().for_each(|(index, line)| {
        let group_index = index % 3;
        current_elf_group[group_index] = line;

        if group_index == 2 {
            elf_groups.push(ElfGroup::new(&current_elf_group));
            current_elf_group = [""; 3];
        }
    });

    elf_groups
        .iter()
        .map(|group| calc_priority(group.badge_type))
        .sum::<u32>()
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::etc::utils;

    #[test]
    fn ascii_chars_distance() {
        let a = 'a';
        let k = 'k';
        let distance = (k as u8) - (a as u8);

        println!("{distance}");
    }

    #[test]
    fn part1() {
        let input = utils::to_multiline_string(vec![
            "vJrwpWtwJgWrhcsFMMfFFhFp",
            "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
            "PmmdzqPrVvPwwTWBwg",
            "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn",
            "ttgJtRGJQctTZtZT",
            "CrZsJsPPZsGzwwsLwLmpwMDw",
        ]);
        assert_eq!(157, solve_part1(input));
    }

    #[test]
    fn part2() {
        let input = utils::to_multiline_string(vec![
            "vJrwpWtwJgWrhcsFMMfFFhFp",
            "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
            "PmmdzqPrVvPwwTWBwg",
            "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn",
            "ttgJtRGJQctTZtZT",
            "CrZsJsPPZsGzwwsLwLmpwMDw",
        ]);
        assert_eq!(70, solve_part2(input));
    }
}
